package com.leinao.util;

import com.google.common.collect.Lists;
import com.leinao.commons.Response;
import com.leinao.config.EnvironmentConfig;
import com.leinao.constant.Constant;
import com.leinao.constant.FileErrorEnum;
import com.leinao.constant.PathTypeEnum;
import com.leinao.file.dto.FileInfoDto;
import com.leinao.file.dto.FilePageDto;
import com.leinao.file.dto.FileProcessDataDto;
import com.leinao.file.dto.FileUploadDto;
import com.leinao.nio.FileCopyTree;
import com.leinao.nio.FileDeleteTree;
import com.leinao.nio.ZipTree;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import java.io.*;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.FileChannel;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.WritableByteChannel;
import java.nio.file.*;
import java.nio.file.attribute.FileAttribute;
import java.nio.file.attribute.PosixFilePermission;
import java.nio.file.attribute.PosixFilePermissions;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.zip.ZipOutputStream;

@Component
public class FileUtil {

	private static Logger logger = LoggerFactory.getLogger(FileUtil.class);

	// 格式化日期时间，记录日志时使用
	private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

	@Autowired
	private EnvironmentConfig env;

	/**
	 * @Description 获取根目录
	 * @param type : 文件删除根目录类型-com.leinao.constant.PathTypeEnum
	 * @author suqiliang
	 * @Date 2018年6月30日 上午10:33:15
	 */
	public String getBaseDir(String userId, Integer type) {
		String path = null;
		if(PathTypeEnum.BASE.getType().equals(type)) {
			path = env.getBasePath() + userId;
		} else if(PathTypeEnum.PROJECT.getType().equals(type)) {
			path = env.getProjectPath() + userId;
		} else if(PathTypeEnum.DATASET.getType().equals(type)) {
			path = env.getDatasetPath() + userId;
		}else if(PathTypeEnum.MODELS.getType().equals(type)){
			path = env.getModelsPath()+ userId;
		}

		if(path == null) {
			logger.error("getBaseDir param type is invalid, type : {}", type);
			throw new RuntimeException("getBaseDir param type is invalid");
		}

		Path userPath = Paths.get(path);
		if (Files.notExists(userPath)) {
			try {
				Files.createDirectories(userPath);
			} catch (AccessDeniedException e) {
				logger.error("getBaseDir AccessDeniedException", e);
				throw new RuntimeException(e);
			} catch (IOException e) {
				logger.error("getBaseDir IOException", e);
				throw new RuntimeException(e);
			} catch (Exception e) {
				logger.error("getBaseDir Exception", e);
				throw e;
			}
		}
		return path;
	}

	/**
	 * @Description 遍历目录下的文件
	 * @param dir: 遍历目录
	 * @param pageNumber: 当前页数
	 * @param pageSize: 每页条数
	 * @return
	 * @author suqiliang
	 * @Date 2018年6月29日 上午11:56:52
	 */
	public Response<FilePageDto> getFileList(String dir, Integer pageNumber, Integer pageSize) {
		// 定义返回结果
		FilePageDto filePage = new FilePageDto(pageNumber, pageSize);
		Path filePath = Paths.get(dir);
		if(Files.notExists(filePath)){
			return Response.ok(filePage);
		}

		try {
			// 使用Files.list()方法会导致windows文件或文件夹无法删除问题
			List<FileInfoDto> fileList = Lists.newArrayList();
			Files.walk(filePath,1).filter((path) -> {
				// 过滤查询路径本身以及.gitkeep文件
				if(filePath.equals(path) || path.endsWith(Constant.HANDLE_GIT_EMPTY_DIR)) {
					return false;
				} else {
					// 设置文件总数
					filePage.setTotalCount(filePage.getTotalCount() + 1);
					return true;
				}
			}).sorted((p1, p2) -> {
				try {
                    /* 过滤软连接和根据修改时间排序会有性能问题，修改为按照文件名排序
					// 软链接不做排序处理
					if(Files.isSymbolicLink(p1) || Files.isSymbolicLink(p2)) {
						return 0;
					}

					// 根据文件最后修改时间倒序
					return Files.getLastModifiedTime(p2).compareTo(Files.getLastModifiedTime(p1));
                    */
                    return p2.toString().compareTo(p1.toString());
				} catch (Exception e) {
					logger.error("getFileList sort file exception", e);
					return 0;
				}
				// 这是分页信息
			}).skip((pageNumber - 1) * pageSize).limit(pageSize).forEachOrdered((file) -> {
				FileInfoDto fileInfoDto = new FileInfoDto();
				fileInfoDto.setFileName(file.getFileName().toString());
				try {
					boolean isSymbolicLink = Files.isSymbolicLink(file);
					fileInfoDto.setSymbolicLink(isSymbolicLink);

					if(Files.isDirectory(file)) {
						fileInfoDto.setDirectory(true);
					} else if(isSymbolicLink) {
						// 虽然isDirectory默认为false，但是该代码不能删除，否则流程进入else，不存在的软链接汇报错
						fileInfoDto.setDirectory(false);
					} else {
						fileInfoDto.setDirectory(false);
						long size = Files.size(file);
						fileInfoDto.setSize(formatFileSize(size));
					}
					if(!isSymbolicLink) {
						fileInfoDto.setUpdateTime(Files.getLastModifiedTime(file).toMillis());
					}
				}  catch (IOException e) {
					logger.error("getFileList IOException", e);
					throw new RuntimeException(e);
				}
				fileList.add(fileInfoDto);
			});

			// 文件列表按照上传时间排序
			filePage.setFileList(fileList);
			filePage.setTotalPage((filePage.getTotalCount() + pageSize - 1) / pageSize);
			return Response.ok(filePage);
		} catch (AccessDeniedException e) {
			logger.error("getFileList AccessDeniedException", e);
			return Response.error(Constant.SUCCESS_CODE, FileErrorEnum.FILE_ACCESS_DENIED.getSeq(), FileErrorEnum.FILE_ACCESS_DENIED.getDesc(), null);
		} catch (IOException e) {
			logger.error("getFileList IOException", e);
			return Response.error(Constant.SUCCESS_CODE, FileErrorEnum.FILE_READ_FAILURE.getSeq(), FileErrorEnum.FILE_READ_FAILURE.getDesc(),  null);
		} catch (Exception e) {
			logger.error("getFileList Exception", e);
			return Response.error(Constant.SUCCESS_CODE, FileErrorEnum.FILE_READ_FAILURE.getSeq(), FileErrorEnum.FILE_READ_FAILURE.getDesc(),  null);
		}
	}

	/**
	 * @Description 新建文件夹
	 * @param dir
	 * @return
	 * @author suqiliang
	 * @Date 2018年6月29日 上午11:56:52
	 */
	public Response<Void> addDir(String dir) {
		Path addPath = Paths.get(dir);
		if (Files.exists(addPath)) {
			return Response.error(Constant.SUCCESS_CODE, FileErrorEnum.FILE_EXIST.getSeq(), FileErrorEnum.FILE_EXIST.getDesc());
		}

		try {
			// 多级文件夹级连创建
			Files.createDirectories(addPath);
			return Response.ok();
		} catch (AccessDeniedException e) {
			logger.error("addDirectory AccessDeniedException", e);
			return Response.error(Constant.SUCCESS_CODE, FileErrorEnum.FILE_ACCESS_DENIED.getSeq(), FileErrorEnum.FILE_ACCESS_DENIED.getDesc());
		} catch (IOException e) {
			logger.error("addDirectory IOException", e);
			return Response.error(Constant.SUCCESS_CODE, FileErrorEnum.FILE_CREATE_FAILURE.getSeq(), FileErrorEnum.FILE_CREATE_FAILURE.getDesc());
		} catch (Exception e) {
			logger.error("addDirectory Exception", e);
			return Response.error(Constant.SUCCESS_CODE, FileErrorEnum.FILE_CREATE_FAILURE.getSeq(), FileErrorEnum.FILE_CREATE_FAILURE.getDesc());
		}
	}

	/**
	 * @Description 删除文件或文件夹
	 * @param filePath: 文件或文件夹路径
	 * @author suqiliang
	 * @Date 2018年6月29日 下午2:33:20
	 */
	public Response<Void> deleteFile(String filePath) {
		try {
			Files.walkFileTree(Paths.get(filePath), new FileDeleteTree());
			return Response.ok();
			// 异常集合：sun.nio.fs.UnixException.translateToIOException
		}  catch (AccessDeniedException e) {
			logger.error("deleteFile AccessDeniedException", e);
			return Response.error(Constant.SUCCESS_CODE, FileErrorEnum.FILE_ACCESS_DENIED.getSeq(), FileErrorEnum.FILE_ACCESS_DENIED.getDesc());
		}  catch (UncheckedIOException e) {
			if(e.getCause() != null && e.getCause() instanceof AccessDeniedException) {
				logger.error("deleteFile UncheckedIOException -> AccessDeniedException", e);
				return Response.error(Constant.SUCCESS_CODE, FileErrorEnum.FILE_ACCESS_DENIED.getSeq(), FileErrorEnum.FILE_ACCESS_DENIED.getDesc());
			} else {
				logger.error("deleteFile UncheckedIOException", e);
				return Response.error();
			}
		} catch (NoSuchFileException e) {
			logger.error("deleteFile NoSuchFileException", e);
			return Response.error(Constant.SUCCESS_CODE, FileErrorEnum.FILE_NOT_EXIST.getSeq(), FileErrorEnum.FILE_NOT_EXIST.getDesc());
		} catch (IOException e) {
			logger.error("deleteFile IOException", e);
			return Response.error(Constant.SUCCESS_CODE, FileErrorEnum.FILE_DELETE_FAILURE.getSeq(), FileErrorEnum.FILE_DELETE_FAILURE.getDesc());
		} catch (Exception e) {
			logger.error("deleteFile Exception", e);
			return Response.error(Constant.SUCCESS_CODE, FileErrorEnum.FILE_DELETE_FAILURE.getSeq(), FileErrorEnum.FILE_DELETE_FAILURE.getDesc());
		}
	}

	/**
	 * @Description 文件重命名
	 * @param dir : 需要复制的文件或文件夹
	 * @param newDir：需要复制的文件或文件夹
	 * @return
	 * @author suqiliang
	 * @Date 2018年6月29日 上午11:56:52
	 */
	public Response<Void> renameFile(String dir, String newDir) {
		try {
			Path dirPath = Paths.get(dir);
			if (Files.notExists(dirPath)) {
				return Response.error(Constant.SUCCESS_CODE, FileErrorEnum.FILE_NOT_EXIST.getSeq(), FileErrorEnum.FILE_NOT_EXIST.getDesc());
			}

			Path newPath = Paths.get(newDir);
			if(Files.exists(newPath)) {
				return Response.error(Constant.SUCCESS_CODE, FileErrorEnum.FILE_EXIST.getSeq(), FileErrorEnum.FILE_EXIST.getDesc());
			}

			Files.move(dirPath, newPath);
			return Response.ok();
		} catch (AccessDeniedException e) {
			logger.error("renameFile AccessDeniedException", e);
			return Response.error(Constant.SUCCESS_CODE, FileErrorEnum.FILE_ACCESS_DENIED.getSeq(), FileErrorEnum.FILE_ACCESS_DENIED.getDesc());
		} catch (IOException e) {
			logger.error("renameFile IOException", e);
			return Response.error(Constant.SUCCESS_CODE, FileErrorEnum.FILE_RENAME_FAILURE.getSeq(), FileErrorEnum.FILE_RENAME_FAILURE.getDesc());
		} catch (Exception e) {
			logger.error("renameFile Exception", e);
			return Response.error(Constant.SUCCESS_CODE, FileErrorEnum.FILE_RENAME_FAILURE.getSeq(), FileErrorEnum.FILE_RENAME_FAILURE.getDesc());
		}
	}

	/**
	 * @Description 文件复制
	 * @param dir : 拷贝的路径
	 * @return
	 * @author suqiliang
	 * @Date 2018年7月2日 上午9:44:49
	 */
	public Response<Void> copyFile(String dir) {
		try {
			Path copyFrom = Paths.get(dir);
			if (Files.notExists(copyFrom)) {
				return Response.error(Constant.SUCCESS_CODE, FileErrorEnum.FILE_NOT_EXIST.getSeq(), FileErrorEnum.FILE_NOT_EXIST.getDesc());
			}

			// 生成需要复制的文件或者文件夹的名称
			Path copyTo = getCopyFileName(dir);
			Files.walkFileTree(copyFrom, Collections.emptySet(), Integer.MAX_VALUE, new FileCopyTree(copyFrom, copyTo));
			return Response.ok();
		} catch (AccessDeniedException e) {
			logger.error("copyFile AccessDeniedException", e);
			return Response.error(Constant.SUCCESS_CODE, FileErrorEnum.FILE_ACCESS_DENIED.getSeq(), FileErrorEnum.FILE_ACCESS_DENIED.getDesc());
		} catch (IOException e) {
			logger.error("copyFile IOException", e);
			return Response.error(Constant.SUCCESS_CODE, FileErrorEnum.FILE_COPY_FAILURE.getSeq(), FileErrorEnum.FILE_COPY_FAILURE.getDesc());
		} catch (Exception e) {
			logger.error("copyFile Exception", e);
			return Response.error(Constant.SUCCESS_CODE, FileErrorEnum.FILE_COPY_FAILURE.getSeq(), FileErrorEnum.FILE_COPY_FAILURE.getDesc());
		}
	}

	/**
	 * @Description 文件复制
	 * @param dirFrom : 需要拷贝的路径
	 * @param dirTo : 拷贝的位置
	 * @return
	 * @author suqiliang
	 * @Date 2018年7月2日 上午9:44:49
	 */
	public Response<String> copyFile(String dirFrom, String dirTo) {
		return copyFile(dirFrom, dirTo, false);
	}

	/**
	 * @Description 文件复制
	 * @param dirFrom : 需要拷贝的路径
	 * @param dirTo : 拷贝的位置
	 *  @param isCover : 拷贝的位置存在是否覆盖
	 * @return
	 * @author suqiliang
	 * @Date 2018年7月2日 上午9:44:49
	 */
	public Response<String> copyFile(String dirFrom, String dirTo, boolean isCover) {
		try {
			Path copyFrom = Paths.get(dirFrom);
			if (Files.notExists(copyFrom)) {
				return Response.error(Constant.SUCCESS_CODE, FileErrorEnum.FILE_NOT_EXIST.getSeq(), FileErrorEnum.FILE_NOT_EXIST.getDesc(), null);
			}
			Path copyTo = Paths.get(dirTo);
			if (!isCover && Files.exists(copyTo)) {
				return Response.error(Constant.SUCCESS_CODE, FileErrorEnum.FILE_EXIST.getSeq(), FileErrorEnum.FILE_EXIST.getDesc(), dirTo);
			} else {
				// 防止根目录不存在的问题
				Files.createDirectories(copyTo);
			}

			Files.walkFileTree(copyFrom, Collections.emptySet(), Integer.MAX_VALUE, new FileCopyTree(copyFrom, copyTo));
			return Response.ok(dirTo);
		} catch (AccessDeniedException e) {
			logger.error("copyFile AccessDeniedException", e);
			return Response.error(Constant.SUCCESS_CODE, FileErrorEnum.FILE_ACCESS_DENIED.getSeq(), FileErrorEnum.FILE_ACCESS_DENIED.getDesc(), null);
		} catch (IOException e) {
			logger.error("copyFile IOException", e);
			return Response.error(Constant.SUCCESS_CODE, FileErrorEnum.FILE_COPY_FAILURE.getSeq(), FileErrorEnum.FILE_COPY_FAILURE.getDesc(), null);
		} catch (Exception e) {
			logger.error("copyFile Exception", e);
			return Response.error(Constant.SUCCESS_CODE, FileErrorEnum.FILE_COPY_FAILURE.getSeq(), FileErrorEnum.FILE_COPY_FAILURE.getDesc(), null);
		}
	}

	/**
	  * @Description 	向指定目录复制文件
	  * @param dirFrom	复制源
	  * @param dirTo	复制目的地
	  * @return
	  * @author:    weiwei
	  * @Date:  2019/5/20 13:42
	  */
	public Response<String> copyOutputFile(String dirFrom, String dirTo) {
		try {
			Path copyFrom = Paths.get(dirFrom);
			if (Files.notExists(copyFrom)) {
				return Response.error(Constant.SUCCESS_CODE, FileErrorEnum.FILE_NOT_EXIST.getSeq(), FileErrorEnum.FILE_NOT_EXIST.getDesc(), null);
			}
			Path copyTo = Paths.get(dirTo);
			if (Files.notExists(copyTo)) {
				return Response.error(Constant.SUCCESS_CODE, FileErrorEnum.FILE_NOT_EXIST.getSeq(), FileErrorEnum.FILE_NOT_EXIST.getDesc(), dirTo);
			}
			Files.walkFileTree(copyFrom, Collections.emptySet(), Integer.MAX_VALUE, new FileCopyTree(copyFrom, copyTo));
			return Response.ok(dirTo);
		} catch (AccessDeniedException e) {
			logger.error("copyFile AccessDeniedException", e);
			return Response.error(Constant.SUCCESS_CODE, FileErrorEnum.FILE_ACCESS_DENIED.getSeq(), FileErrorEnum.FILE_ACCESS_DENIED.getDesc(), null);
		} catch (IOException e) {
			logger.error("copyFile IOException", e);
			return Response.error(Constant.SUCCESS_CODE, FileErrorEnum.FILE_COPY_FAILURE.getSeq(), FileErrorEnum.FILE_COPY_FAILURE.getDesc(), null);
		} catch (Exception e) {
			logger.error("copyFile Exception", e);
			return Response.error(Constant.SUCCESS_CODE, FileErrorEnum.FILE_COPY_FAILURE.getSeq(), FileErrorEnum.FILE_COPY_FAILURE.getDesc(), null);
		}
	}

	/***
	 * @Description 下载单个文件
	 * @param path : 下载文件路径
	 * @param outputStream : 输出流
	 * @param fileProcessData : 文件处理中间数据
	 * @author lizi
	 * @date 2018/11/22 上午10:19
	 */
	public void downloadFile(String path, OutputStream outputStream, FileProcessDataDto fileProcessData) {
		try {
			Path downloadPath = Paths.get(path);
			if (Files.notExists(downloadPath)) {
				logger.error("downloadPath not exists, path : {}", path);
				return;
			}

			long fileSize = Files.size(downloadPath);
			fileProcessData.setBandWidth(BigDecimal.valueOf(fileSize));
			InputStream fis = Files.newInputStream(downloadPath);
			ReadableByteChannel srcChannel = Channels.newChannel(fis);
			WritableByteChannel destChannel = Channels.newChannel(outputStream);
			ByteBuffer buffer = ByteBuffer.allocate(10 * 1024);
			while(srcChannel.read(buffer) != -1){
				buffer.flip();
				while(buffer.hasRemaining()){
					destChannel.write(buffer);
				}
				buffer.clear();
			}

			srcChannel.close();
			destChannel.close();
			fis.close();
		} catch (AccessDeniedException e) {
			logger.error("downloadFile AccessDeniedException", e);
		} catch (FileNotFoundException e) {
			logger.error("downloadFile FileNotFoundException", e);
		} catch (IOException e) {
			logger.error("downloadFile IOException", e);
		} catch (Exception e) {
			logger.error("downloadFile Exception", e);
		}
	}

	/***
	 * @Description 下载批量文件
	 * @param dirs : 下载文件路径数组
	 * @param userHome : 用户目录
	 * @param outputStream : 输出流
	 * @param fileProcessData : 文件处理中间数据
	 * @author lizi
	 * @date 2018/11/22 上午10:19
	 */
	public void batchDownloadFile(String[] dirs, String userHome, OutputStream outputStream, FileProcessDataDto fileProcessData) {
		try {
			ZipOutputStream zos = new ZipOutputStream(outputStream);
			// 需要同时下载的多个文件
			for (String dir : dirs) {
				Path searchPath = Paths.get(userHome + dir);
				if (Files.notExists(searchPath)) {
					logger.error("batchDownloadFile not exists, path : {}", userHome + dir);
					continue;
				}
				Files.walkFileTree(searchPath, new ZipTree(searchPath.getParent(), zos, fileProcessData));
			}
			zos.close();
		} catch (AccessDeniedException e) {
			logger.error("batchDownloadFile AccessDeniedException", e);
		} catch (FileNotFoundException e) {
			logger.error("batchDownloadFile FileNotFoundException", e);
		} catch (IOException e) {
			logger.error("batchDownloadFile IOException", e);
		} catch (Exception e) {
			logger.error("batchDownloadFile Exception", e);
		}
	}

	/**
	 * @Description 文件上传
	 * @param fileUploadDto : 上传的文件信息
	 * @param fileProcessData : 文件处理中间数据
	 * @author lizi
	 * @date 2018/11/22 上午10:22
	 */
	public Response<Void> uploadFile(FileUploadDto fileUploadDto, FileProcessDataDto fileProcessData) {
		try {
			MultipartFile uploadFile = fileUploadDto.getFile();
			// 文件夹创建
			if(uploadFile == null) {
				return Response.ok();
			}

			// 文件保存
			String userId = fileUploadDto.getUserId();
			String dir = fileUploadDto.getDir();
			dir = getBaseDir(userId, fileUploadDto.getType())  + dir;
			Path dirPath;
			String originalFilename = uploadFile.getOriginalFilename().trim();
			if(originalFilename.contains(File.separator))  {
				dirPath = Paths.get(dir + originalFilename.substring(0, originalFilename.lastIndexOf(File.separator)));
			} else {
				dirPath = Paths.get(dir);
			}

			// 上传之前先确保文件夹存在创建
			if (Files.notExists(dirPath)) {
				// 多级文件夹级连创建
				Files.createDirectories(dirPath);
			}

			String fileName = dir + File.separator + originalFilename;

			// 存在同名文件则覆盖，不用判断文件是否存在
			ByteBuffer data = ByteBuffer.allocate(10 * 1024);
			ReadableByteChannel inChannel = Channels.newChannel(uploadFile.getInputStream());
			FileChannel outChannel = new FileOutputStream(fileName).getChannel();

			int read = inChannel.read(data);
			while(read > 0) {
				data.flip();
				outChannel.write(data);
				data.clear();
				read = inChannel.read(data);
				fileProcessData.setBandWidth(fileProcessData.getBandWidth().add(BigDecimal.valueOf(read)));
			}

			outChannel.close();
			inChannel.close();
			return Response.ok();
		} catch (AccessDeniedException e) {
			logger.error("uploadFile AccessDeniedException", e);
			return Response.error(Constant.SUCCESS_CODE, FileErrorEnum.FILE_ACCESS_DENIED.getSeq(), FileErrorEnum.FILE_ACCESS_DENIED.getDesc());
		} catch (IOException e) {
			logger.error("uploadFile IOException", e);
			return Response.error(Constant.SUCCESS_CODE, FileErrorEnum.FILE_UPLOAD_ERROR.getSeq(), FileErrorEnum.FILE_UPLOAD_ERROR.getDesc());
		} catch (Exception e) {
			logger.error("uploadFile Exception", e);
			return Response.error(Constant.SUCCESS_CODE, FileErrorEnum.FILE_UPLOAD_ERROR.getSeq(), FileErrorEnum.FILE_UPLOAD_ERROR.getDesc());
		}
	}

	/**
	 * 新建.gitkeep文件
	 *
	 * @param filePath 文件夹目录
	 */
	public boolean initGitKeepFile(String filePath) {
		boolean initFlag = true;
		try {
			Path path = Paths.get(filePath);
			if(Files.list(path).count() == 0) {
				filePath += filePath.endsWith(File.separator) ? Constant.HANDLE_GIT_EMPTY_DIR : File.separator + Constant.HANDLE_GIT_EMPTY_DIR;
				path = Paths.get(filePath);
				Set<PosixFilePermission> permissions = new HashSet<>();
				permissions.add(PosixFilePermission.OWNER_READ);
				permissions.add(PosixFilePermission.OWNER_WRITE);
				permissions.add(PosixFilePermission.GROUP_READ);
				permissions.add(PosixFilePermission.GROUP_WRITE);
				permissions.add(PosixFilePermission.OTHERS_READ);
				permissions.add(PosixFilePermission.OTHERS_WRITE);
				FileAttribute<Set<PosixFilePermission>> fileAttribute = PosixFilePermissions.asFileAttribute(permissions);
				Files.createFile(path, fileAttribute);
			}
		} catch (Exception e) {
			logger.info("initGitKeepFile failed", e);
			initFlag = false;
		}
		return initFlag;
	}

	/**
	 * 统计文件或文件夹大小是否超限
	 *
	 * @param path 统计路径
	 * @param sizeList 记录已统计文件大小
	 */
	public void statisticalFolderSize(Path path, List<Long> sizeList, Long maxSize) {
		try {
			if (!Files.isDirectory(path)) {
				sizeList.set(0, sizeList.get(0) + Files.size(path));
				return;
			}

			Files.list(path).forEach(action -> {
				// 循环前校验文件夹已统计内容大小
				if(sizeList.get(0) > maxSize) {
					return;
				}
				statisticalFolderSize(action, sizeList, maxSize);
			});
		} catch (Exception e) {
			logger.error("statisticalFolderSize path : {}", path);
			logger.error("statisticalFolderSize error", e);
		}
	}

	/**
	 * @Description 获取复制之后的文件或文件夹名称
	 * @param dir : 需要复制的文件或者文件夹名称
	 * @author lizi
	 * @Date 2018年7月2日 上午9:44:49
	 */
	private Path getCopyFileName(String dir) {
		String prefix;
		String postfix = "";
		if (dir.contains(Constant.CUT_PERIOD)) {
			// 有后缀名的文件处理
			prefix = dir.substring(0, dir.lastIndexOf(Constant.CUT_PERIOD));
			postfix = dir.substring(dir.lastIndexOf(Constant.CUT_PERIOD));
		} else if(dir.endsWith(File.separator)) {
			// 以/结尾的目录处理
			prefix = dir.substring(0, dir.length() - 1);
		} else {
			// 目录处理
			prefix = dir;
		}
		Path copyTo = Paths.get(prefix + "_副本" + postfix);
		if (Files.exists(copyTo)) {
			// 某文件存在复制副本
			int n = 2;
			copyTo = Paths.get(prefix + "_副本 (" + n + ")" + postfix);
			while (Files.exists(copyTo)) {
				n = n + 1;
				copyTo = Paths.get(prefix + "_副本 (" + n + ")" + postfix);
			}
		}
		return copyTo;
	}

	/**
	 * @Description 文件大小格式化
	 * @param size : 文件大小
	 * @author lizi
	 * @Date 2018年7月2日 上午9:44:49
	 */
	private String formatFileSize(long size) {
		DecimalFormat decimalFormat = new DecimalFormat("0.00");
		decimalFormat.setRoundingMode(RoundingMode.DOWN);
		if (size >= 1024 * 1024 && size < 1024 * 1024 * 1024) {
			return decimalFormat.format(size / 1024.0 / 1024.0) + "MB";
		} else if (size >= 1024 * 1024 * 1024) {
			return decimalFormat.format(size / 1024.0 / 1024.0 / 1024.0) + "G";
		} else if(size >= 1024 && size < 1024 * 1024) {
			return decimalFormat.format(size / 1024.0) + "KB";
		} else {
			return size + "B";
		}
	}
}
